/******************************************************************************
                         COPYRIGHT 1955-2008 by BUPT ISC.
                        北京邮电大学信息安全中心 版权所有
                           Web: http://www.bupt.edu.cn
 *******************************************************************************/
/*******************************************************************************
  文件名： conf_helper.c
  项目：   
  模块：   common
  作者：   huangwei
  版本：   0.2
  创建于:  2009-03-20
  描述：
    配置文件解析模块

  修改历史：
  日期：    修改人：  修改描述：
  09/04/13   jiangwen  修改freeConfigNode函数，释放Hash表中该节点占用的内存，解决该
                      模块存在的内存泄漏的问题
  09/05/14   huangwei  修改findInConfigCacheByKey函数，避免了潜在的内存越界访问可能性
 *********************************************************************************/

#include <string.h>
#include <malloc.h>
#include <sys/types.h>
#include <time.h>
#include <sys/stat.h>

#include "digest.h"
#include "conf_helper.h"
#include "string_utils.h"
#include "log.h"

static int compareKey(void *pstNodeOld, void *pstNodeNew);
static int chooseKey(void *pstNode, int iSize);
static void freeConfigNode(void *data);

/******************************************************************************
  函数名：initConfigCache
  功能描述：
    初始化配置文件并将配置文件读入配置缓存

  输入参数：
    const char     * 配置文件名
    CONFIG_CACHE_T * 未初始化的配置缓存指针  

  输出参数：
    无

  返回值：
    int  OK                 初始化配置缓存成功
         CONFIG_NOT_EXIST   配置文件不存在
         CONFIG_OPEN_FAILED 配置文件读取失败
 *****************************************************************************/
int initConfigCache(const char *pcFileName, CONFIG_CACHE_T *pstConfigCache)
{
    long int lFileLength;       /* 配置文件大小 */
    struct stat stConfigFile;   /* 配置文件信息 */
    char line[MAX_COLS] = {""};        /* 配置行 */
    char *pcFile;               /* 配置文件内容 */
    int iLine;                  /* 配置文件行数 */
    FILE *pstConfigFile;        /* 配置文件指针 */

    /* 一次性读取配置文件到内存 */
    if (stat(pcFileName, &stConfigFile) == -1)
    {
        perror(pcFileName);
        return CONFIG_NOT_EXIST;
    }
    else
    {
        lFileLength = stConfigFile.st_size;
        if((pstConfigFile = fopen(pcFileName, "r")) != NULL)
        {
            pcFile = (char *)malloc(lFileLength * sizeof(char));
            fread(pcFile, lFileLength, 1, pstConfigFile);
            /*生成配置文件的md5摘要值*/
            generateDigest(pstConfigCache->acUpdateDigest, pcFile, lFileLength);
            fclose(pstConfigFile);
        }
        else
        {
            perror(pcFileName);
            return CONFIG_OPEN_FAILED;
        }        
    }

    
    /* 获取文件行数 */
    iLine = countWords(pcFile, ITEM_SP); 

    /* 释放内存中的配置文件内容，后续将采用逐行读入配置文件的策略 */
    if (pcFile != NULL)
    {
        free(pcFile);
    }

    /* 初始化CONFIG_CACHE_T */
    HASH_TABLE_T *pstHashTable = newHashTable((int)(iLine / HASH_FACTOR), compareKey, chooseKey, freeConfigNode);

    /* 按行读取配置项内容到Hash表 */ 
    if((pstConfigFile = fopen(pcFileName, "r")) != NULL)
    {
        char *name;
        char *value;

        while ( fgets(line, sizeof(line), pstConfigFile) != (char *)0)
        {

            /* 删除行首注释 */
            trimComment(line, '#');      
            /* 提取名值对 */
            if ((value = strchr(line, '=')) != NULL)
            {
                int iSeparator = strlen(line) - strlen(value);
                name = substr(line, 0, iSeparator);
                value = substr(value, 1, strlen(value) - 1); 
                /* 删除行尾注释 */
                trimComment(value, '#'); 
                CONFIG_NODE_T *pstConfigNode = (CONFIG_NODE_T *)malloc(sizeof(CONFIG_NODE_T));
                bzero(pstConfigNode->acName, MAX_NAME);
                bzero(pstConfigNode->acValue, MAX_VALUE);
                /* 拷贝名值对时检查缓冲区大小，防止缓冲区溢出攻击 */
                strncpy(pstConfigNode->acName, trim(name), strlen(name) < MAX_NAME ? strlen(name): MAX_NAME);
                strncpy(pstConfigNode->acValue, trim(value), strlen(value) < MAX_VALUE ? strlen(value) : MAX_VALUE);

                addToHash(pstHashTable, pstConfigNode);

                if (name != NULL)
                {
                    free(name);
                    name = NULL;
                }
                if (value != NULL)
                {
                    free(value);
                    value = NULL;
                }
            }

            
        } 
        
        if (pstConfigFile != NULL)
        {
            fclose(pstConfigFile);
        }

    }
    else
    {
        perror(pcFileName);
        return CONFIG_OPEN_FAILED;
    }        

   
    /* 更新CONFIG_CACHE的所有元素 */
    /* TODO 配置缓存摘要生成算法 */
    pstConfigCache->pstHashTable = pstHashTable;
    strncpy(pstConfigCache->acFile, pcFileName, strlen(pcFileName));
    pstConfigCache->iUpdateType = CONFIG_UPDATE_MANUAL;
    gettimeofday(&(pstConfigCache->pstLastUpdateTime), NULL);

    return 0;    
}

static int compareKey(void *pstNodeOld, void *pstNodeNew)
{
    int iOldLen = strlen(pstNodeOld);
    int iNewLen = strlen(pstNodeNew);
    int iLen = iOldLen > iNewLen ? iOldLen : iNewLen;
    return strncmp(((CONFIG_NODE_T *)pstNodeOld)->acName, 
            ((CONFIG_NODE_T *)pstNodeNew)->acName, 
            iLen);
}

static int chooseKey(void *pstNode, int iSize)
{
    char *pcKey = ((CONFIG_NODE_T *)pstNode)->acName;
    
    unsigned int iHash = 0;
    int          i     = 0;
    for ( ; i < strlen(pcKey); i++)
    {
        iHash += pcKey[i];
        iHash += (iHash << 10);
        iHash ^= (iHash >> 6);
    }

    iHash += (iHash << 3);
    iHash ^= (iHash >> 11);
    iHash += (iHash << 15);

    return iHash % iSize;
}

static void freeConfigNode(void *data)
{
   /* do nothing here*/ 
    CONFIG_NODE_T *pstTmp = (CONFIG_NODE_T *)data;
    free(pstTmp);
}

/******************************************************************************
  函数名：destroyConfigCache
  功能描述：
    清理配置参数缓存

  输入参数：
    CONFIG_CACHE_T * 配置参数缓存指针

  输出参数：
    无

  返回值：
    0   函数调用成功，无错误发生
 *****************************************************************************/
int destroyConfigCache(CONFIG_CACHE_T *pstConfigCache)
{
    if (pstConfigCache != NULL)
    {
        deleteHash(pstConfigCache->pstHashTable, ENABLE_FREE_DATA_CB);
    }
    /*TODO some other cleanups */
    return 0;
}

/******************************************************************************
  函数名：findInConfigCacheByKey
  功能描述：
    在配置缓存中的Hash表中按照配置项名称查找配置项的值
    注意：返回的数组需要手工释放内存

  输入参数：
    CONFIG_CACHE_T * 配置参数缓存指针
    const char     * 配置项名称字符串

  输出参数：
    无

  返回值：
    char ** 找到的配置项值数组
 *****************************************************************************/
char **findInConfigCacheByKey(CONFIG_CACHE_T *pstConfigCache, const char *pcKey)
{
    char **pcValues; /* 返回值数组 */
    if (pcKey == NULL)
    {
        return NULL;
    }

    if (pstConfigCache == NULL)
    {
        log_r(LOG_WARNING, "配置参数缓存未正确初始化!\n");
        return NULL; 
    }

    HASH_TABLE_T *pstHashTable = pstConfigCache->pstHashTable;

    /* modified in 2009-05-14 */
    CONFIG_NODE_T *pstKeyNode = (CONFIG_NODE_T *)malloc(sizeof(CONFIG_NODE_T));
    if (pstKeyNode == NULL)
    {
        log_r(LOG_WARNING, "malloc for KeyNode failed!\n");
        return NULL;
    }

    /* modified in 2009-05-18 */
    memset(pstKeyNode->acName, 0, strlen(pstKeyNode->acName));
    strncpy(pstKeyNode->acName, pcKey, strlen(pcKey));

    HASH_NODE_T *pstHashNode = findInHash(pstHashTable, (void *)pstKeyNode);
    
    /* added in 2009-05-14 */
    free(pstKeyNode);
    pstKeyNode = NULL;


    HASH_NODE_T *pstHashNodeTemp = pstHashNode;
    int iNodeCount = 0; 
    while (pstHashNodeTemp != NULL) /* 计算同一Key下对应的配置节点数 */
    {
        iNodeCount++;
        pstHashNodeTemp = pstHashNodeTemp->pstNext;
    }
    
    pcValues = (char **)calloc(iNodeCount + 1, sizeof(char *));
    int i = 0;
    while (pstHashNode != NULL)
    {
        char *value = ((CONFIG_NODE_T *)pstHashNode->pData)->acValue;
        pcValues[i] = strndup(value, strlen(value));
        i++;
        pstHashNode = pstHashNode->pstNext;
    }

    return pcValues;
}

/******************************************************************************
  函数名：freeConfigCacheValues
  功能描述：
    释放findInConfigCacheByKey返回值指针所指向的内存空间
  输入参数：
    char **待释放的findInConfigCacheByKey返回值指针

  输出参数：
    无

  返回值：
    无
 *****************************************************************************/

void freeConfigCacheValues(char **values)
{
    while (values != NULL)
    {
        while( *values != NULL)
        {
            free(*values);
            *values = NULL;
        }
        free(values);
        values = NULL;
    }

    return;
}

void debugConfigCache(CONFIG_CACHE_T *pstConfigCache)
{
    dbgprint("Config File Name: %s\n", pstConfigCache->acFile);
    /*TODO 时间打印函数 */
    dbgprint("Config Cache Last Update Time: %s\n",(char *)asctime(localtime(&(pstConfigCache->pstLastUpdateTime.tv_sec))));
    dbgprint("Config Cache Last Update Digest: %s\n", pstConfigCache->acUpdateDigest);
    dbgprint("Config Cache Update Method: %d\n", pstConfigCache->iUpdateType);

    HASH_TABLE_T *pstHash = pstConfigCache->pstHashTable;
    int i;
    HASH_NODE_T *pstBucket;

    for(i = 0; i < pstHash->iSize; i++)
    {
        dbgprint("[%d] ", i);
        pstBucket = pstHash->pstTable[i];

        while (pstBucket != NULL)
        {
            dbgprint("-> [%10p] ", pstBucket);
            dbgprint("-> %s=%s ", ((CONFIG_NODE_T *)pstBucket->pData)->acName, ((CONFIG_NODE_T *)pstBucket->pData)->acValue); 
            pstBucket = pstBucket->pstNext;
        }

        dbgprint("\n");
    }

    dbgprint("\n");

#if _DEBUG_
    /* resizeHash Test */
    dbgprint("resizeHash Test\n");
    pstHash = resizeHash(pstHash, pstHash->iSize * 2);
    pstConfigCache->pstHashTable = pstHash;
#endif

}
